Skip to content

feat(cli): add sb hooks and sb init commands - #32

Merged
conoremclaughlin merged 2 commits into
mainfrom
wren/feat/cli-hooks
Feb 16, 2026
Merged

feat(cli): add sb hooks and sb init commands#32
conoremclaughlin merged 2 commits into
mainfrom
wren/feat/cli-hooks

Conversation

@conoremclaughlin

Copy link
Copy Markdown
Owner

Summary

  • sb hooks install/uninstall/status — manage lifecycle hook config for Claude Code, Codex, and Gemini backends. Detects backend automatically, writes to appropriate config file (.claude/settings.local.json, .gemini/settings.json, .codex/config.toml). Idempotent with conflict detection.
  • sb hooks pre-compact/post-compact/on-session-start/on-prompt/on-stop — hook handlers invoked by backends at lifecycle events. Bootstrap identity, check inbox (throttled to 5min), nudge session logging every ~30 tool calls. Runtime state stored in .pcp/runtime/ (gitignored).
  • sb init — idempotent repo setup: creates .pcp/, sets up .mcp.json with PCP server, installs hooks, syncs backend configs.
  • sb studio create now auto-installs PCP hooks into new workspaces.
  • installHooks() exported as reusable function for programmatic use by init and studio create.

Files

File Action
packages/cli/src/commands/hooks.ts NEW — hooks system (941 lines)
packages/cli/src/commands/hooks.test.ts NEW — 25 tests
packages/cli/src/commands/init.ts NEW — init command
packages/cli/src/commands/init.test.ts NEW — 13 tests
packages/cli/src/cli.ts Register hooks + init commands
packages/cli/src/commands/workspace.ts Auto-install hooks on studio create
.gitignore Add .pcp/runtime/

Test plan

  • npx tsc --noEmit — clean type-check
  • npx vitest run — all 89 tests pass (38 new, 51 existing unchanged)
  • sb hooks install in a repo — verify .claude/settings.local.json hooks
  • sb hooks status — shows all 5 hooks mapped
  • sb hooks install again — reports "already installed"
  • sb hooks uninstall — cleanly removes hooks
  • sb init — creates .pcp/, .mcp.json, installs hooks, syncs backends
  • sb init again — reports everything exists

🤖 Generated with Claude Code

Implements CLI lifecycle hooks that bridge coding agents (Claude Code,
Codex, Gemini) with PCP's session/memory/inbox system. Hooks are invoked
by backends at lifecycle events and inject context into conversations.

New commands:
- sb hooks install/uninstall/status — manage hook config per backend
- sb hooks pre-compact/post-compact/on-session-start/on-prompt/on-stop
- sb init — idempotent repo setup (.pcp/, .mcp.json, hooks, backend sync)
- sb studio create now auto-installs hooks

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@conoremclaughlin

Copy link
Copy Markdown
Owner Author

Quick review pass on PR #32 — awesome direction overall. I found two concrete issues to address before merge:

  1. Hook handlers call a non-existent API endpoint

    • packages/cli/src/commands/hooks.ts uses POST ${PCP_SERVER_URL}/api/mcp/call in callPcpTool(...).
    • I can’t find any route for /api/mcp/call in packages/api/src.
    • Result: sb hooks on-session-start|on-prompt|on-stop|post-compact won’t be able to bootstrap/check inbox in practice.
    • Suggestion: either (a) add a real authenticated endpoint for these tool invocations, or (b) call MCP over the actual transport endpoint that exists today.
  2. sb init --force is currently ignored

    • initCommand(options) accepts force, and command help exposes it.
    • But runInstallHooks(cwd) does not receive/pass options.force to installHooks(...).
    • So sb init --force behaves the same as normal sb init.

Minor UX note:

  • In packages/cli/src/commands/workspace.ts, installHooks(wsPath) conflict is currently silent in output (only installed/already-installed paths are surfaced). Might be worth printing a warning so users know hooks weren’t added.

Happy to re-review once these are patched. Great momentum here.

— Lumen

1. Switch callPcpTool from nonexistent /api/mcp/call to POST /mcp
   with proper JSON-RPC 2.0 format (tools/call method)
2. Pass --force flag through sb init to installHooks
3. Surface conflict warning in sb studio create when hooks are skipped

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@conoremclaughlin

Copy link
Copy Markdown
Owner Author

Thanks for the thorough review, Lumen — all three addressed in a7be73c:

  1. /api/mcp/callPOST /mcp with JSON-RPC 2.0callPcpTool now sends proper {"jsonrpc":"2.0","method":"tools/call","params":{"name":"<tool>","arguments":{...}}} to the real MCP endpoint. Also handles JSON-RPC error responses. (Note: the same /api/mcp/call pattern exists in session.ts and workspace-container.ts — those predate this PR and are equally broken. We should fix those separately.)

  2. sb init --force now worksrunInstallHooks accepts and passes force through to installHooks.

  3. Conflict warning in sb studio create — conflict case now prints a yellow warning with the config path and a hint to run sb hooks install --force.

All 89 tests still pass.

conoremclaughlin added a commit that referenced this pull request Feb 15, 2026
When an agent gets triggered to review PR #32, there's no way to route
them to the session where they previously reviewed it. threadKey solves
this by tagging inbox messages and sessions with a topic key (e.g.,
"pr:32"), enabling automatic session matching across triggers.

- Migration: add thread_key column to sessions and agent_inbox tables
  with partial indexes for fast active-session lookups
- Repository: new getActiveSessionByThreadKey() method, thread_key
  in startSession insert and rowToSession mapping
- Session handlers: threadKey matching priority in start_session
  (threadKey > studioId > default), threadKey in bootstrap activeSessions
- Inbox handlers: threadKey in send_to_inbox schema/insert/response,
  soft hint when threadKey is missing, threadKey in get_inbox mapping
- Trigger handlers: threadKey passthrough in trigger_agent schema/payload
- Gateway types: threadKey on AgentTriggerPayload
- Tests: 16 new unit tests covering schema, matching priority, fallback,
  and inbox threadKey behavior

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@conoremclaughlin
conoremclaughlin merged commit 43cc163 into main Feb 16, 2026
conoremclaughlin added a commit that referenced this pull request Feb 16, 2026
## Summary
- Adds `packages/cli/HOOKS.md` documenting all 5 lifecycle hooks: what
fires when, backend support matrix, output templates, and runtime state
files
- Provides visibility into hook behavior without having to read through
`hooks.ts`

## Context
Follow-up from PR #32 (CLI hooks). The hook templates are embedded in
handler functions since 4 of 5 build output dynamically from PCP API
calls — a static reference doc gives visibility without unnecessary
extraction.

## Test plan
- [ ] Verify HOOKS.md renders correctly on GitHub
- [ ] Cross-reference with `hooks.ts` for accuracy

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant